package com.fc.im.netty.message;

import com.fc.im.netty.exception.BodyTooLongException;
import com.fc.im.netty.exception.HeaderCheckFailedException;

import java.nio.ByteBuffer;

/**
 * Created by ChanJiabo on 2017/1/17.
 */
public class Message {

    private MessageHeader header;
    private byte[] body;

    /**
     * 根据bytebuffer创建消息对象
     * @param byteBuffer
     * @return
     */
    public static Message createMessage(ByteBuffer byteBuffer) {
        Message result = new Message();
        result.header = MessageHeader.createHeader(byteBuffer);
        byte[] bytes = new byte[byteBuffer.capacity() - byteBuffer.position()];
        byteBuffer.get(bytes);
        result.body = bytes;

        //校验
        byte check = (byte) result.header.getCheckCodePart();
        for (byte b : result.body) {
            check = (byte) (check ^ b);
        }
        if(check != result.header.getCheckCode()){
            throw new HeaderCheckFailedException("Message check failed");
        }
        return result;
    }

    /**
     * 创建消息对象
     *
     * @param header
     * @param body
     * @return
     */
    public static Message createMessage(MessageHeader header, byte[] body) {
        Message result = new Message();
        result.header = header;
        result.body = body;
        return result;
    }

    /**
     * 创建一个默认消息头的消息
     *
     * @param body
     * @return
     */
    public static Message createMessageInDefaultHeader(byte[] body) {
        return createMessage(new MessageHeader(), body);
    }



    /**
     * 构建message
     * @return
     */
    public ByteBuffer build() throws BodyTooLongException{
        if( this.body.length > Short.MAX_VALUE) {
            throw new BodyTooLongException("Body-length too long,Max is " + Short.MAX_VALUE);
        }
        ByteBuffer byteBuffer = encodeHeader((short) this.body.length);
        byteBuffer.flip();
        return byteBuffer;
    }

    /**
     * 只打包Header，Body部分独立传输.
     * @param bodyLength
     * @return
     */
    private ByteBuffer encodeHeader(final short bodyLength){
        // 1> total length size
        short length = 0;

        // 2> check code length
        length += 1;
        // 3> version length
        length += 1;
        // 4> message type length
        length += 1;
        // 5> module length
        // length += 1;
        // 6> order length
        // length += 1;
        // 7> encrypt type length
        length += 1;
        // 8> compress type length
        length += 1;

        length += bodyLength;

        ByteBuffer result = ByteBuffer.allocate(2 + length - bodyLength);

        // 9> length
        result.putShort(length);

        // 10> check code
        byte checkCode = header.getCheckCodePart();

        for (byte b : body) {
            checkCode = (byte) (checkCode ^ b);
        }
        result.put(checkCode);

        // 11> vsersion
        result.put(header.getVersion());

        // 12> message type
        result.put(header.getMsgType());

        // 13> module
        // result.put(header.getModule());

        // 14> order
        // result.put(header.getOrder());

        // 15> encrypt type
        result.put(header.getEncryptType());

        // 16 > Compress type
        result.put(header.getCompressType());

        return result;
    }

    public byte[] getBody() {
        return body;
    }

    public MessageHeader getHeader() {
        return header;
    }

    public static Message heartBeatMessage(){
        MessageHeader header = new MessageHeader();
        header.setMsgType(MessageType.HEART_BEAT);
        Message message = Message.createMessage(header, new byte[]{});
        return message;
    }
}
